#include <gtest/gtest.h>

#include <string>
#include <optional>

TEST(OptionalTest, test){

	//easy use for primitive
	{
		std::optional<int> v = 1;
		ASSERT_TRUE(v);
		std::cout << v.value() << std::endl;
	}

	//easy use for object
	{
		std::optional<std::string> s = "hello";
		ASSERT_TRUE(s);
		std::cout << s.value() << std::endl;
	}

	//easy use for ref
	{
		std::string str = "world";
		std::optional<std::reference_wrapper<std::string>> s_ref = str;
		ASSERT_TRUE(s_ref);
		std::cout << s_ref.value().get() << std::endl;
	}

	//easy use for null option
	{
		std::optional<int> n = std::nullopt;
		ASSERT_FALSE(n);
	}

	//Move 
	{
		auto old = std::make_optional<std::string>("hello");
		ASSERT_TRUE(old);

		auto new_o = std::optional<std::string>(std::move(old));
		ASSERT_TRUE(new_o);
		ASSERT_TRUE(old); //old is not-empty  after moved
		std::cout  << "move operation"
			<< "  new:[" << new_o.value()  
			<< "] old:[" << old.value() 
			<< "] deref:[" << *new_o
			<< "]"
			<< std::endl;
	}
	{
		auto old = std::make_optional<std::string>("hello");
		ASSERT_TRUE(old);

		auto new_o = std::move(old);
		ASSERT_TRUE(new_o && new_o.has_value());
		ASSERT_TRUE(old && old.has_value()); //old is not-empty  after moved
		std::cout  << "move operation"
			<< "  new:[" << new_o.value()  
			<< "] old:[" << old.value() 
			<< "] deref:[" << *new_o
			<< "]"
			<< std::endl;
	}

	//copy one
	{
		auto str = std::make_optional<std::string>("hello");
		ASSERT_TRUE(str);
		{
			auto same_o = std::optional<std::string>(str); //copy ctor
			ASSERT_TRUE(same_o && same_o.has_value());
			ASSERT_TRUE(str && str.has_value());
			ASSERT_EQ(str, same_o);
		}

		//copy one 
		{
			auto same_o = str;
			ASSERT_TRUE(same_o && same_o.has_value());
			ASSERT_TRUE(str && str.has_value());
			ASSERT_EQ(str, same_o);
		}
	}

	//reset 
	{
		auto str = std::make_optional<std::string>("hello");
		ASSERT_TRUE(str);
		str.reset();
		ASSERT_FALSE(str);
	}

	//function args
	{
		auto str = std::make_optional<std::string>("hello");
		auto f = [](std::optional<std::string> & opt){
			if(opt){
				std::cout << "opt:" << *opt << std::endl;
			}
			else{
				std::cout << "opt is empty"  << std::endl;
			}
		};
		f(str);
	}
}

TEST(ReferenceWrapperTest, test){
	std::string s = "string";
	std::reference_wrapper<std::string> ref_s = std::ref(s); // std::cref(s);
	std::optional<std::reference_wrapper<std::string>> o_ref = ref_s;
	std::cout 
			<< s 
			<< ref_s.get()
			<< o_ref.value().get()
			<< std::endl;

}
